package org.nox.helloseries.rest.client;

import java.io.IOException;
import java.nio.charset.Charset;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.nox.helloseries.configuration.CustomConfiguration;
import org.nox.helloseries.configuration.CustomConfigurationKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

public abstract class AbstractRESTRequestService
{

    private static final Logger LOG = LoggerFactory.getLogger( AbstractRESTRequestService.class );

    private static final String BASE_URL = CustomConfiguration.get().getString( CustomConfigurationKey.URL_HELLOSERIES_REST );

    private static final int HTTP_CODE_OK = 200;

    private static final int DEFAULT_TIMEOUT_MS = 600000; // 10min

    /**
     * Méthode GET
     * 
     * @param resourceName
     * @param clazz
     * @return
     */
    public <T> T executeGETRequest( String resourceName, Class<T> clazz )
    {
        return executeGETRequest( resourceName, clazz, null );
    }

    /**
     * Méthode GET
     * 
     * @param resourceName
     * @param typeRef
     * @return
     */
    public <T> T executeGETRequest( String resourceName, TypeReference<T> typeRef )
    {
        return executeGETRequest( resourceName, null, typeRef );
    }

    /**
     * Méthode GET
     * 
     * @param resourceName
     * @param clazz
     * @param typeRef
     * @return
     */
    private <T> T executeGETRequest( String resourceName, Class<T> clazz, TypeReference<T> typeRef )
    {
        LOG.info( "Création du client HTTP" );

        HttpGet request = new HttpGet( BASE_URL + getParentResourceName() + "/" + resourceName );
        LOG.info( "Appel au service REST {}", getParentResourceName() + "/" + resourceName );
        try
        {
            HttpResponse response = getHttpClient().execute( request );

            if ( response.getStatusLine().getStatusCode() == HTTP_CODE_OK )
            {
                return getContenuResponseHTTP( response, clazz, typeRef );
            }
        }
        catch ( IOException e )
        {
            LOG.error( "", e );
        }

        return null;
    }

    /**
     * Méthode POST
     * 
     * @param resourceName
     * @param clazz
     * @param typeRef
     * @return
     */
    protected String executePOSTRequest( String resourceName, Object parameter )
    {
        LOG.info( "Création du client HTTP" );

        HttpPost request = new HttpPost( BASE_URL + getParentResourceName() + "/" + resourceName );

        LOG.info( "Appel au service REST {}", getParentResourceName() + "/" + resourceName );
        try
        {
            // Gestion des paramétres, envoi sous forme JSON
            StringEntity input = new StringEntity( getJSONString( parameter ) );
            input.setContentType( "application/json" );
            request.setEntity( input );

            HttpResponse response = getHttpClient().execute( request );

            if ( response.getStatusLine().getStatusCode() == HTTP_CODE_OK )
            {
                return IOUtils.toString( response.getEntity().getContent() );
            }
        }
        catch ( IOException e )
        {
            LOG.error( "", e );
        }

        return null;
    }

    private String getJSONString( Object parameter )
    {
        try
        {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString( parameter );
        }
        catch ( IOException e )
        {
            LOG.error( "Impossible d'obtenir le JSON ", e );
        }
        return null;
    }

    private <T> T getContenuResponseHTTP( HttpResponse response, Class<T> clazz, TypeReference<T> typeRef )
    {
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();

            // Récupération du contenu
            String jsonContent = IOUtils.toString( response.getEntity().getContent(), Charset.defaultCharset() );
            if ( clazz == null )
            {
                return objectMapper.readValue( jsonContent, typeRef );
            }
            else
            {
                return objectMapper.readValue( jsonContent, clazz );
            }
        }
        catch ( IOException e )
        {
            LOG.error( "Lecture de la réponse impossible ", e );
        }
        return null;
    }

    /**
     * @return un client http par defaut
     */
    private HttpClient getHttpClient()
    {
        // Configuration des timeout
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout( DEFAULT_TIMEOUT_MS ).setConnectTimeout( DEFAULT_TIMEOUT_MS ).setSocketTimeout( DEFAULT_TIMEOUT_MS ).build();

        return HttpClientBuilder.create().setDefaultRequestConfig( requestConfig ).build();
    }

    public abstract String getParentResourceName();
}
